﻿/**
* Fixed drag & drop plugin for jQuery UI 1.8.2
* which has bug in IE9 
*/
/*
var msie9 = false;

//IE9 dnd fix (jQuery UI Mouse (<= 1.8.5) doesnt support IE9)
if ($.ui && $.browser.msie && parseInt($.browser.version, 10) >= 9) {
    msie9 = true;
    var mm = $.ui.mouse.prototype._mouseMove;
    $.ui.mouse.prototype._mouseMove = function(b) { b.button = 1; mm.apply(this, [b]); }
}
*/

/*
* Validator
*/
; (function($) {

    $.tools = $.tools || { version: '@VERSION' };

    // globals
    var typeRe = /\[type=([a-z]+)\]/,
		numRe = /^-?[0-9]*(\.[0-9]+)?$/,

    // http://net.tutsplus.com/tutorials/other/8-regular-expressions-you-should-know/
		emailRe = /^([a-z0-9_\.\-\+]+)@([\da-z\.\-]+)\.([a-z\.]{2,6})$/i,
		urlRe = /^(https?:\/\/)?[\da-z\.\-]+\.[a-z\.]{2,6}[#\?\/\w \.\-=]*$/i,
		v;

    v = $.tools.validator = {

        conf: {
            grouped: false, 				// show all error messages at once inside the container 
            effect: 'default', 		        // show/hide effect for error message. only 'default' is built-in
            errorClass: 'error-border',     // input field class name in case of validation error		

            // when to check for validity?
            inputEvent: null, 			    // change, blur, keyup, null 
            errorInputEvent: 'keyup',       // change, blur, keyup, null
            formEvent: 'submit',            // submit, null

            lang: 'en', 					// default language for error messages 
            message: '<div/>',
            messageAttr: 'datamessage',    // name of the attribute for overridden error message
            messageClass: 'validator-error', 	        // error message element's class name
            offset: [0, 0],
            position: 'center right',
            singleError: false, 			// validate all inputs at once
            speed: 'normal'				    // message's fade-in speed			
        },


        /* The Error Messages */
        messages: {
            "*": { en: " " }
        },

        localize: function(lang, messages) {
            $.each(messages, function(key, msg) {
                v.messages[key] = v.messages[key] || {};
                v.messages[key][lang] = msg;
            });
        },

        localizeFn: function(key, messages) {
            v.messages[key] = v.messages[key] || {};
            $.extend(v.messages[key], messages);
        },

        /** 
        * Adds a new validator 
        */
        fn: function(matcher, msg, fn) {

            // no message supplied
            if ($.isFunction(msg)) {
                fn = msg;

                // message(s) on second argument
            } else {
                if (typeof msg == 'string') { msg = { en: msg }; }
                this.messages[matcher.key || matcher] = msg;
            }

            // check for "[type=xxx]" (not supported by jQuery)
            var test = typeRe.exec(matcher);
            if (test) { matcher = isType(test[1]); }

            // add validator to the arsenal
            fns.push([matcher, fn]);
        },

        /* Add new show/hide effect */
        addEffect: function(name, showFn, closeFn) {
            effects[name] = [showFn, closeFn];
        }

    };

    /* calculate error message position relative to the input */
    function getPosition(trigger, el, conf) {
        if (conf.position === 'center right') {
            if (Eu2 && Eu2.Culture && Eu2.Culture.isRtl()) {
                conf.position = 'center left';
            }
        }
        // get origin top/left position
        var position = trigger.position(),
            top = position.top,
			left = position.left,
			pos = conf.position.split(/,?\s+/),
			y = pos[0],
			x = pos[1];

        top -= el.outerHeight() - conf.offset[0];
        left += trigger.outerWidth() + conf.offset[1];

        // adjust Y		
        var height = el.outerHeight() + trigger.outerHeight();
        if (y == 'center') { top += height / 2; }
        if (y == 'bottom') { top += height; }

        // adjust X
        var width = trigger.outerWidth();
        
        if (x == 'center') { left -= (width + el.outerWidth()) / 2; }
        if (x == 'left') { left -= (width + el.outerWidth()); }

        return { top: top, left: left };
    }



    // $.is("[type=xxx]") or $.filter("[type=xxx]") not working in jQuery 1.3.2 or 1.4.2
    function isType(type) {
        function fn() {
            return this.getAttribute("type") == type;
        }
        fn.key = "[type=" + type + "]";
        return fn;
    }


    var fns = [], effects = {

        'default': [

        // show errors function
			function(errs) {

			    var conf = this.getConf();

			    // loop errors
			    $.each(errs, function(i, err) {

			        // add error class	
			        var input = err.input;
			        input.addClass(conf.errorClass);

			        // get handle to the error container
			        var msg = input.data("msg.el");

			        // create it if not present
			        if (!msg) {
			            //msg = $(conf.message).addClass(conf.messageClass).appendTo(document.body);
			            msg = $(conf.message).addClass(conf.messageClass).insertAfter(input);
			            input.data("msg.el", msg);
			        }

			        // clear the container 
			        msg.css({ visibility: 'hidden', display: 'none' }).find("span").remove();

			        // populate messages
			        $.each(err.messages, function(i, m) {
			            $("<span/>").html(m).appendTo(msg);
			        });

			        // make sure the width is sane (not the body's width)
			        if (msg.outerWidth() == msg.parent().width()) {
			            msg.add(msg.find("p")).css({ display: 'inline' });
			        }

			        // insert into correct position (relative to the field)
			        var pos = getPosition(input, msg, conf);

			        msg.css({ visibility: 'visible', position: 'absolute', top: pos.top, left: pos.left })
						.fadeIn(conf.speed);
			    });


			    // hide errors function
			}, function(inputs) {

			    var conf = this.getConf();
			    inputs.removeClass(conf.errorClass).each(function() {
			        var msg = $(this).data("msg.el");
			        if (msg) { msg.css({ visibility: 'hidden' }); }
			    });
			}
		]
    };


    /* sperial selectors */
    $.each("email,url,number".split(","), function(i, key) {
        $.expr[':'][key] = function(el) {
            return el.getAttribute("type") === key;
        };
    });


    /* 
    oninvalid() jQuery plugin. 
    Usage: $("input:eq(2)").oninvalid(function() { ... });
    */
    $.fn.oninvalid = function(fn) {
        return this[fn ? "bind" : "trigger"]("OI", fn);
    };


    /******* built-in HTML5 standard validators *********/

    v.fn(":email,[etype=email]", "Please enter a valid email address", function(el, v) {
        return !v || emailRe.test(v);
    });

    v.fn(":url", "Please enter a valid URL", function(el, v) {
        return !v || urlRe.test(v);
    });

    v.fn(":number,[etype=number]", "Please enter a numeric value.", function(el, v) {
        var group = $.format.locale().number.groupingSeparator,
            dec = $.format.locale().number.decimalSeparator;

        // Floating point pattern with grouping separator and decimal separator sign
        //var pattern = '^[-]?((([0-9]{1,3}([' + group + '][0-9]{3})*([' + dec + '][0-9]+)?|[' + dec + '][0-9]+))|((\d+))|([0-9]*([' + dec + '][0-9]+)?))$';

        // Floating point pattern with decimal separator and no grouping separator sign
        var pattern = '^-?[0-9]*([' + dec + '][0-9]+)?$';
        var reg = new RegExp(pattern, 'i');
        return reg.test(v);
    });
    v.fn("[enumber=true]", "Please enter a numeric value.", function(el, v) {
        var group = $.format.locale().number.groupingSeparator,
            dec = $.format.locale().number.decimalSeparator;

        // Floating point pattern with grouping separator and decimal separator sign
        //var pattern = '^[-]?((([0-9]{1,3}([' + group + '][0-9]{3})*([' + dec + '][0-9]+)?|[' + dec + '][0-9]+))|((\d+))|([0-9]*([' + dec + '][0-9]+)?))$';

        // Floating point pattern with decimal separator and no grouping separator sign
        var pattern = '^-?[0-9]*([' + dec + '][0-9]+)?$';
        var reg = new RegExp(pattern, 'i');
        return reg.test(v);
    });
    v.fn("[etype=formatednumber]", "Please enter a numeric value.", function(el, v) {
        var group = $.format.locale().number.groupingSeparator,
            dec = $.format.locale().number.decimalSeparator;

        // Floating point pattern with grouping separator and decimal separator sign
        var pattern = '^[-]?((([0-9]{1,3}([' + group + '][0-9]{3})*([' + dec + '][0-9]+)?|[' + dec + '][0-9]+))|((\d+))|([0-9]*([' + dec + '][0-9]+)?))$';

        // Floating point pattern with decimal separator and no grouping separator sign
        //var pattern = '^-?[0-9]*([' + dec + '][0-9]+)?$';
        var reg = new RegExp(pattern, 'i');
        return reg.test(v);
    });
    /*
    v.fn("[etype=number]", "Please enter a numeric value.", function(el, v) {
    var group = $.format.locale().number.groupingSeparator,
    dec = $.format.locale().number.decimalSeparator;

        // Floating point pattern with grouping separator and decimal separator sign
    //var pattern = '^[-]?((([0-9]{1,3}([' + group + '][0-9]{3})*([' + dec + '][0-9]+)?|[' + dec + '][0-9]+))|((\d+))|([0-9]*([' + dec + '][0-9]+)?))$';

        // Floating point pattern with decimal separator and no grouping separator sign
    var pattern = '^-?[0-9]*([' + dec + '][0-9]+)?$';
    var reg = new RegExp(pattern, 'i');
    return reg.test(v);
    });
    */
    v.fn('[etype=date]', 'Please enter a correct date.', function(el, v) {
        //http://panther.sugarcrm.com/wiki/index.php?title=Custom_validation_of_datetime_fields
        var dateFormat = $.format.locale().date.format,
            validDatePattern = null,
            day = null,
            month = null,
            year = null,
            part = null,
            time = null,
            separator = null; //

        if (v == "")
            return false;
        // Strip seperators from dateformat. Simplify dateformat as we only need order of day month and year in dateformat.
        dateFormat = dateFormat.toLowerCase();
        validDatePattern = /^[dmy]+[^dmy][dmy]+[^dmy][dmy]+/;
        if (validDatePattern.test(dateFormat)) {
            separator = dateFormat.match(/[^dmy]/g);
            if (separator && separator.length == 2) {
                separator = separator[0];
            }
            part = dateFormat.split(/[^dmy]/);
            dateFormat = part[0].substr(0, 1) + part[1].substr(0, 1) + part[2].substr(0, 1);
        }
        else {
            // Can not process this dateformat.
            alert("Cannot process dateformat " + dateFormat);
            return "";
        }

        // Build patterns which is used to test whether date is in specified dateformat.
        switch (dateFormat) {
            case 'dmy':
            case 'mdy':
                // Accept 9x9x99, 9x9x9999, 99x99x99, 99x99x9999 (9 is any digit, x is any non-digit) as valid date patterns.
                //validDatePattern = /^\d{1,2}[^\d]\d{1,2}[^\d]\d{2,4}/;

                // Accept: 9x9x9999, 99x99x9999
                validDatePattern = new RegExp('^\\d{1,2}[' + separator + ']\\d{1,2}[' + separator + ']\\d{4}$')
                break;
            case 'ymd':
                // Accept 9999x99x99, 9999x9x9 (9 is any digit, x is any non-digit) as valid date patterns.
                //validDatePattern = /^\d{2,4}[^\d]\d{1,2}[^\d]\d{1,2}/;
                validDatePattern = new RegExp('^\\d{4}[' + separator + ']\\d{1,2}[' + separator + ']\\d{1,2}$')
                break;
        }
        if (validDatePattern.test(v)) {
            // Split field in seperate fields for day, month and year. Split on seperators.
            var part = v.split(new RegExp('[' + separator + ']', 'g'));

            try {
                // Pattern has been matched.
                switch (dateFormat) {
                    case 'dmy':
                        part[0] = (part[0].indexOf('0') == 0 && part[0].length == 2) ? part[0].substring(1) : part[0];
                        day = parseInt(part[0]);
                        part[1] = (part[1].indexOf('0') == 0 && part[1].length == 2) ? part[1].substring(1) : part[1];
                        month = parseInt(part[1]);
                        year = parseInt(part[2]);
                        break;
                    case 'mdy':
                        part[1] = (part[1].indexOf('0') == 0 && part[1].length == 2) ? part[1].substring(1) : part[1];
                        day = parseInt(part[1]);
                        part[0] = (part[0].indexOf('0') == 0 && part[0].length == 2) ? part[0].substring(1) : part[0];
                        month = parseInt(part[0]);
                        year = parseInt(part[2]);
                        break;
                    case 'ymd':
                        year = parseInt(part[0]);
                        part[2] = (part[2].indexOf('0') == 0 && part[2].length == 2) ? part[2].substring(1) : part[2];
                        day = parseInt(part[2]);
                        part[1] = (part[1].indexOf('0') == 0 && part[1].length == 2) ? part[1].substring(1) : part[1];
                        month = parseInt(part[1]);
                        break;
                }

                var d = new Date();
                d.setFullYear(year, month - 1, day)

                return d.getDate() == day && (d.getMonth() + 1) == month && d.getFullYear() == year;
            } catch (e) {
                return false;
            }
        }
        return false;
    });
    v.fn("[max]", "Please enter a value smaller than $1", function(el, v) {

        // skip empty values and dateinputs
        if (v === '' || $.tools.dateinput && el.is(":date")) { return true; }

        var max = el.attr("max");
        return eNumber(v) <= eNumber(max) ? true : [max];
    });

    v.fn("[min]", "Please enter a value larger than $1", function(el, v) {

        // skip empty values and dateinputs
        if (v === '' || $.tools.dateinput && el.is(":date")) { return true; }

        var min = el.attr("min");
        return eNumber(v) >= eNumber(min) ? true : [min];
    });

    v.fn("[required]", "Please complete this mandatory field.", function(el, v) {
        if (el.is(":checkbox")) { return el.is(":checked"); }
        return !!v;
    });

    v.fn("[pattern]", function(el) {
        var p = new RegExp("^" + el.attr("pattern") + "$");
        return p.test(el.val());
    });
    function Validator(inputs, form, conf) {

        // private variables
        var self = this,
			 fire = form.add(self);

        // make sure there are input fields available
        inputs = inputs.not(":button, :image, :reset, :submit");

        // utility function
        function pushMessage(to, matcher, returnValue) {

            // only one message allowed
            if (!conf.grouped && to.length) { return; }

            // the error message
            var msg;

            // substitutions are returned
            if (returnValue === false || $.isArray(returnValue)) {
                msg = v.messages[matcher.key || matcher] || v.messages["*"];
                msg = msg[conf.lang] || v.messages["*"].en;

                // substitution
                var matches = msg.match(/\$\d/g);

                if (matches && $.isArray(returnValue)) {
                    $.each(matches, function(i) {
                        msg = msg.replace(this, returnValue[i]);
                    });
                }

                // error message is returned directly
            } else {
                msg = returnValue[conf.lang] || returnValue;
            }

            to.push(msg);
        }


        // API methods  
        $.extend(self, {

            getConf: function() {
                return conf;
            },

            getForm: function() {
                return form;
            },

            getInputs: function() {
                return inputs;
            },

            reflow: function() {
                inputs.each(function() {
                    var input = $(this),
						 msg = input.data("msg.el");

                    if (msg) {
                        var pos = getPosition(input, msg, conf);
                        msg.css({ top: pos.top, left: pos.left });
                    }
                });
                return self;
            },

            /* @param e - for internal use only */
            invalidate: function(errs, e) {

                // errors are given manually: { fieldName1: 'message1', fieldName2: 'message2' }
                if (!e) {
                    var errors = [];
                    $.each(errs, function(key, val) {
                        var input = inputs.filter("[name='" + key + "']");
                        if (input.length) {

                            // trigger HTML5 ininvalid event
                            input.trigger("OI", [val]);

                            errors.push({ input: input, messages: [val] });
                        }
                    });

                    errs = errors;
                    e = $.Event();
                }

                // onFail callback
                e.type = "onFail";
                fire.trigger(e, [errs]);

                // call the effect
                if (!e.isDefaultPrevented()) {
                    effects[conf.effect][0].call(self, errs, e);
                }

                return self;
            },

            reset: function(els) {
                els = els || inputs;
                els.removeClass(conf.errorClass).each(function() {
                    var msg = $(this).data("msg.el");
                    if (msg) {
                        msg.remove();
                        $(this).data("msg.el", null);
                    }
                }).unbind(conf.errorInputEvent || '');
                return self;
            },

            destroy: function() {
                form.unbind(conf.formEvent).unbind("reset.V");
                inputs.unbind(conf.inputEvent || '').unbind("change.V");
                return self.reset();
            },
            // Shorthand for checkValidity() function
            isValid: function(els, e) {
                els = els || inputs;
                els = els.not(":disabled");
                if (!els.length) { return true; }

                e = e || $.Event();
                return this.checkValidity(els, e);
            },
            //{{{  checkValidity() - flesh and bone of this tool
            /* @returns boolean */
            checkValidity: function(els, e) {

                els = els || inputs;
                els = els.not(":disabled");
                if (!els.length) { return true; }

                e = e || $.Event();

                // onBeforeValidate
                e.type = "onBeforeValidate";
                fire.trigger(e, [els]);
                if (e.isDefaultPrevented()) { return e.result; }

                if (conf.errorInputEvent && conf.errorInputEvent.indexOf('.') != -1)
                    conf.errorInputEvent = conf.errorInputEvent.substring(0, conf.errorInputEvent.indexOf('.'));

                var errs = [],
					 event = conf.errorInputEvent + ".v";

                // loop trough the inputs
                els.not(":radio:not(:checked)").each(function() {

                    // field and it's error message container						
                    var msgs = [],
						 el = $(this).unbind(event).data("messages", msgs);

                    // loop all validator functions
                    $.each(fns, function() {
                        var fn = this, match = fn[0];

                        // match found
                        if (el.filter(match).length) {

                            // execute a validator function
                            var returnValue = fn[1].call(self, el, el.val());


                            // validation failed. multiple substitutions can be returned with an array
                            if (returnValue !== true) {

                                // onBeforeFail
                                e.type = "onBeforeFail";
                                fire.trigger(e, [el, match]);
                                if (e.isDefaultPrevented()) { return false; }

                                // overridden custom message
                                var msg = el.attr(conf.messageAttr);
                                if (msg) {
                                    msgs = [msg];
                                    return false;
                                } else {
                                    pushMessage(msgs, match, returnValue);
                                }
                            }
                        }
                    });

                    if (msgs.length) {

                        errs.push({ input: el, messages: msgs });

                        // trigger HTML5 ininvalid event
                        el.trigger("OI", [msgs]);

                        // begin validating upon error event type (such as keyup) 
                        if (conf.errorInputEvent) {
                            el.bind(event, function(e) {
                                self.checkValidity(el, e);
                            });
                        }
                    }

                    if (conf.singleError && errs.length) { return false; }

                });


                // validation done. now check that we have a proper effect at hand
                var eff = effects[conf.effect];
                if (!eff) { throw "Validator: cannot find effect \"" + conf.effect + "\""; }

                // errors found
                if (errs.length) {
                    self.invalidate(errs, e);
                    return false;

                    // no errors
                } else {

                    // call the effect
                    eff[1].call(self, els, e);

                    // onSuccess callback
                    e.type = "onSuccess";
                    fire.trigger(e, [els]);

                    els.unbind(event);
                }

                return true;
            }
            //}}} 

        });

        // callbacks	
        $.each("onBeforeValidate,onBeforeFail,onFail,onSuccess".split(","), function(i, name) {

            // configuration
            if ($.isFunction(conf[name])) {
                $(self).bind(name, conf[name]);
            }

            // API methods				
            self[name] = function(fn) {
                $(self).bind(name, fn);
                return self;
            };
        });


        // form validation
        if (conf.formEvent) {
            form.bind(conf.formEvent, function(e) {
                if (!self.checkValidity($(this).find('input'), e)) {
                    return e.preventDefault();
                }
            });
        }

        // form reset
        form.bind("reset.V", function() {
            self.reset();
        });

        // disable browser's default validation mechanism
        if (inputs[0] && inputs[0].validity) {
            inputs.each(function() {
                this.oninvalid = function() {
                    return false;
                };
            });
        }

        // Web Forms 2.0 compatibility
        if (form[0]) {
            form[0].checkValidity = self.checkValidity;
        }

        // input validation               
        if (conf.inputEvent) {
            inputs.bind(conf.inputEvent, function(e) {
                self.checkValidity($(this), e);
            });
        }

        // checkboxes, selects and radios are checked separately
        inputs.filter(":checkbox, select").filter("[required]").bind("change.V", function(e) {
            var el = $(this);
            if (this.checked || (el.is("select") && $(this).val())) {
                effects[conf.effect][1].call(self, el, e);
            }
        });

        var radios = inputs.filter(":radio").change(function(e) {
            self.checkValidity(radios, e);
        });

        // reposition tooltips when window is resized
        $(window).resize(function() {
            self.reflow();
        });
    }


    // jQuery plugin initialization
    $.fn.validator = function(conf) {

        var instance = this.data("validator");

        // destroy existing instance
        if (instance) {
            instance.destroy();
            this.removeData("validator");
        }

        // configuration
        conf = $.extend(true, {}, v.conf, conf);
        if (conf.inputEvent && conf.inputEvent.indexOf('.') < 0) {
            conf.inputEvent += '.eValidator';
        }

        if (conf.errorInputEvent && conf.errorInputEvent.indexOf('.') < 0) {
            conf.errorInputEvent += '.eValidator';
        }

        if (conf.formEvent && conf.formEvent.indexOf('.') < 0) {
            conf.formEvent += '.eValidator';
        }

        // selector is a form		
        if (this.is("form")) {
            return this.each(function() {
                var form = $(this);
                instance = new Validator(form.find(":input"), form, conf);
                form.data("validator", instance);
            });

        } else {
            instance = new Validator(this, this.eq(0).closest("form"), conf);
            return this.data("validator", instance);
        }

    };

})(jQuery);
/*
* etip jQuery plugin  base on jPoshy Tip (http://vadikom.com/tools/poshy-tip-jquery-plugin-for-stylish-tooltips/, Copyright 2010, Vasil Dinkov, http://vadikom.com/)
* 
*/

(function($) {

    var tips = [],
        ieregex = /(msie) ([\w.]+)/,
		reBgImage = /^url\(["']?([^"'\)]*)["']?\);?$/i,
		rePNG = /\.png$/i,
		ie6 = false,
        match;

    match = ieregex.exec(navigator.userAgent);
    ie6 = match && match[1] && match[2] && match[2] < 7;

    // make sure the tips' position is updated on resize
    function handleWindowResize() {
        $.each(tips, function() {
            this.refresh(true);
        });
    }
    $(window).resize(handleWindowResize);

    $.etip = function(elm, options) {
        this.$elm = $(elm);
        this.opts = $.extend({}, $.fn.etip.defaults, options, { className: 'etooltip' });
        this.$tip = $(['<div class="', this.opts.className, '">',
                '<div class="tip-header hidden"><span class="span-close"></span></div>',
				'<div class="tip-inner tip-bg-image"></div>',
				'<div class="tip-arrow tip-arrow-top tip-arrow-right tip-arrow-bottom tip-arrow-left"></div>',
			'</div>'].join(''));
        this.$arrow = this.$tip.find('div.tip-arrow');
        this.$inner = this.$tip.find('div.tip-inner');
        this.disabled = false;
        this.init();
        this.isShowOnCondition = false;
    };

    $.etip.prototype = {
        init: function() {
            tips.push(this);

            // save the original 'etiptext' atribute and a reference to the etip object
            this.$elm.data('etip', this);

            // hook element events
            switch (this.opts.showOn) {
                case 'hover':
                    this.$elm.bind({
                        'mouseenter.etip': $.proxy(this.mouseenter, this),
                        'mouseleave.etip': $.proxy(this.mouseleave, this)
                    });
                    if (this.opts.alignTo == 'cursor')
                        this.$elm.bind('mousemove.etip', $.proxy(this.mousemove, this));
                    if (this.opts.allowTipHover)
                        this.$tip.hover($.proxy(this.clearTimeouts, this), $.proxy(this.hide, this));
                    break;
                case 'focus':
                    this.$elm.bind({
                        'focus.etip': $.proxy(this.show, this),
                        'blur.etip': $.proxy(this.hide, this)
                    });
                    break;
            }
            if (typeof this.opts.alignC === 'string') {
                var cAlign = this.opts.alignC.toLowerCase();
                if (cAlign == 'left' || cAlign == 'center' || cAlign == 'right') {
                    this.$inner.css('text-align', cAlign);
                }
            }

            if (this.opts.opacity && this.opts.opacity >= 0 && this.opts.opacity <= 1)
                this.$tip.css('opacity', this.opts.opacity);

            if (this.opts.showCloseButton) {
                this.$tip
                    .find('.tip-header')
                    .removeClass('hidden')
                    .children('.span-close')
                    .bind('click', $.proxy(this.hide, this));
            }
        },
        mouseenter: function(e) {
            if (this.disabled)
                return true;

            this.clearTimeouts();
            this.showTimeout = setTimeout($.proxy(this.show, this), this.opts.showTimeout);
        },
        mouseleave: function() {
            if (this.disabled)
                return true;

            this.clearTimeouts();
            this.hideTimeout = setTimeout($.proxy(this.hide, this), this.opts.hideTimeout);
        },
        mousemove: function(e) {
            if (this.disabled)
                return true;

            this.eventX = e.pageX;
            this.eventY = e.pageY;
            if (this.opts.followCursor && this.$tip.data('active')) {
                this.calcPos();
                this.$tip.css({ left: this.pos.l, top: this.pos.t });
                if (this.pos.arrow)
                    this.$arrow[0].className = 'tip-arrow tip-arrow-' + this.pos.arrow;
            }
        },
        show: function() {
            if (this.disabled || this.$tip.data('active'))
                return;

            this.reset();
            this.update();
            this.display();
        },
        hide: function() {
            if (this.disabled || !this.$tip.data('active'))
                return;

            this.display(true);
        },
        reset: function() {
            this.$tip.queue([]).detach().css('visibility', 'hidden').data('active', false);
            this.$inner.find('*').etip('hide');
            if (this.opts.fade)
                this.$tip.css('opacity', this.opacity);
            this.$arrow[0].className = 'tip-arrow tip-arrow-top tip-arrow-right tip-arrow-bottom tip-arrow-left';
        },
        update: function(content) {
            if (this.disabled)
                return;

            var async = content !== undefined;
            if (async) {
                if (!this.$tip.data('active'))
                    return;
            } else {
                content = this.opts.text;
            }

            this.$inner.contents().detach();
            var self = this;
            this.$inner.append(
				typeof content == 'function' ?
					content.call(this.$elm[0], function(newContent) {
					    self.update(newContent);
					}) :
					content == null ? this.$elm.attr('etiptext') : content
			);

            this.refresh(async);
        },
        refresh: function(async) {
            if (this.disabled)
                return;

            if (async) {
                if (!this.$tip.data('active'))
                    return;
                // save current position as we will need to animate
                var currPos = { left: this.$tip.css('left'), top: this.$tip.css('top') };
            }

            // reset position to avoid text wrapping, etc.
            this.$tip.css({ left: 0, top: 0 }).appendTo(document.body);

            // save default opacity
            if (this.opacity === undefined)
                this.opacity = this.$tip.css('opacity');

            // check for images - this code is here (i.e. executed each time we show the tip and not on init) due to some browser inconsistencies
            var bgImage = this.$tip.css('background-image').match(reBgImage),
				arrow = this.$arrow.css('background-image').match(reBgImage);

            if (bgImage) {
                var bgImagePNG = rePNG.test(bgImage[1]);
                // fallback to background-color/padding/border in IE6 if a PNG is used
                if (ie6 && bgImagePNG) {
                    this.$tip.css('background-image', 'none');
                    this.$inner.css({ margin: 0, border: 0, padding: 0 });
                    bgImage = bgImagePNG = false;
                } else {
                    this.$tip.prepend('<table border="0" cellpadding="0" cellspacing="0"><tr><td class="tip-top tip-bg-image" colspan="2"><span></span></td><td class="tip-right tip-bg-image" rowspan="2"><span></span></td></tr><tr><td class="tip-left tip-bg-image" rowspan="2"><span></span></td><td></td></tr><tr><td class="tip-bottom tip-bg-image" colspan="2"><span></span></td></tr></table>')
						.css({ border: 0, padding: 0, 'background-image': 'none', 'background-color': 'transparent' })
						.find('.tip-bg-image').css('background-image', 'url("' + bgImage[1] + '")').end()
						.find('td').eq(3).append(this.$inner);
                }
                // disable fade effect in IE due to Alpha filter + translucent PNG issue
                if (bgImagePNG && !$.support.opacity)
                    this.opts.fade = false;
            }
            // IE arrow fixes
            if (arrow && !$.support.opacity) {
                // disable arrow in IE6 if using a PNG
                if (ie6 && rePNG.test(arrow[1])) {
                    arrow = false;
                    this.$arrow.css('background-image', 'none');
                }
                // disable fade effect in IE due to Alpha filter + translucent PNG issue
                this.opts.fade = false;
            }

            var $table = this.$tip.find('table');
            if (ie6) {
                // fix min/max-width in IE6
                this.$tip[0].style.width = '';
                $table.width('auto').find('td').eq(3).width('auto');
                var tipW = this.$tip.width(),
					minW = parseInt(this.$tip.css('min-width')),
					maxW = parseInt(this.$tip.css('max-width'));
                if (!isNaN(minW) && tipW < minW)
                    tipW = minW;
                else if (!isNaN(maxW) && tipW > maxW)
                    tipW = maxW;
                this.$tip.add($table).width(tipW).eq(0).find('td').eq(3).width('100%');
            } else if ($table[0]) {
                // fix the table width if we are using a background image
                $table.width('auto').find('td').eq(3).width('auto').end().end().width(this.$tip.width()).find('td').eq(3).width('100%');
            }
            this.tipOuterW = this.$tip.outerWidth();
            this.tipOuterH = this.$tip.outerHeight();

            this.calcPos();
            if (this.isShowOnCondition)
                return;

            // position and show the arrow image
            if (arrow && this.pos.arrow) {
                this.$arrow[0].className = 'tip-arrow tip-arrow-' + this.pos.arrow;
                this.$arrow.css('visibility', 'inherit');
            }

            if (async)
                this.$tip.css(currPos).animate({ left: this.pos.l, top: this.pos.t }, 200);
            else
                this.$tip.css({ left: this.pos.l, top: this.pos.t });
        },
        display: function(hide) {
            var active = this.$tip.data('active');
            if (active && !hide || !active && hide || this.isShowOnCondition)
                return;
			
            // Set z-index as colorbox to tip display upper colorbox
            if ($.colorbox && $.colorbox.isOpen()) {
                this.$tip.css('z-index', $('#colorbox').css('z-index'));
            }
			
        	this.$tip.stop();
            if ((this.opts.slide && this.pos.arrow || this.opts.fade) && (hide && this.opts.hideAniDuration || !hide && this.opts.showAniDuration)) {
                var from = {}, to = {};
                // this.pos.arrow is only undefined when alignX == alignY == 'center' and we don't need to slide in that rare case
                if (this.opts.slide && this.pos.arrow) {
                    var prop, arr;
                    if (this.pos.arrow == 'bottom' || this.pos.arrow == 'top') {
                        prop = 'top';
                        arr = 'bottom';
                    } else {
                        prop = 'left';
                        arr = 'right';
                    }
                    var val = parseInt(this.$tip.css(prop));
                    from[prop] = val + (hide ? 0 : this.opts.slideOffset * (this.pos.arrow == arr ? -1 : 1));
                    to[prop] = val + (hide ? this.opts.slideOffset * (this.pos.arrow == arr ? 1 : -1) : 0);
                }
                if (this.opts.fade) {
                    from.opacity = hide ? this.$tip.css('opacity') : 0;
                    to.opacity = hide ? 0 : this.opacity;
                }
                this.$tip.css(from).animate(to, this.opts[hide ? 'hideAniDuration' : 'showAniDuration']);
            }
            hide ? this.$tip.queue($.proxy(this.reset, this)) : this.$tip.css('visibility', 'inherit');
            this.$tip.data('active', !active);
        },
        disable: function() {
            this.reset();
            this.disabled = true;
        },
        enable: function() {
            this.disabled = false;
        },
        destroy: function() {
            this.reset();
            this.$tip.remove();
            this.$elm.unbind('etip').removeData('etip');
            tips.splice($.inArray(this, tips), 1);
        },
        clearTimeouts: function() {
            if (this.showTimeout) {
                clearTimeout(this.showTimeout);
                this.showTimeout = 0;
            }
            if (this.hideTimeout) {
                clearTimeout(this.hideTimeout);
                this.hideTimeout = 0;
            }
        },
        calcPos: function() {
            var verArrowAlign = ['left', 'center', 'right'],
				hozArrowAlign = ['top', 'middle', 'bottom'];
            var pos = { l: 0, t: 0, arrow: '' },
				$win = $(window),
				win = {
				    l: $win.scrollLeft(),
				    t: $win.scrollTop(),
				    w: $win.width(),
				    h: $win.height()
				}, xL, xC, xR, yT, yC, yB;
            if (this.opts.alignTo == 'cursor') {
                xL = xC = xR = this.eventX;
                yT = yC = yB = this.eventY;
            } else { // this.opts.alignTo == 'target'
                var elmOffset = this.$elm.offset(),
					elm = {
					    l: elmOffset.left,
					    t: elmOffset.top,
					    w: this.$elm.outerWidth(),
					    h: this.$elm.outerHeight()
					};
                xL = elm.l + (this.opts.alignX != 'inner-right' ? 0 : elm.w); // left edge
                xC = xL + Math.floor(elm.w / 2); 			// h center
                xR = xL + (this.opts.alignX != 'inner-left' ? elm.w : 0); // right edge
                yT = elm.t + (this.opts.alignY != 'inner-bottom' ? 0 : elm.h); // top edge
                yC = yT + Math.floor(elm.h / 2); 			// v center
                yB = yT + (this.opts.alignY != 'inner-top' ? elm.h : 0); // bottom edge
            }

            // keep in viewport and calc arrow position
            switch (this.opts.alignX) {
                case 'right':
                case 'inner-left':
                    pos.l = xR + this.opts.offsetX;
                    if (pos.l + this.tipOuterW > win.l + win.w)
                        pos.l = win.l + win.w - this.tipOuterW;
                    if (this.opts.alignX == 'right' || this.opts.alignY == 'center')
                        pos.arrow = 'left';
                    break;
                case 'center':
                    pos.l = xC - Math.floor(this.tipOuterW / 2);
                    if (pos.l + this.tipOuterW > win.l + win.w)
                        pos.l = win.l + win.w - this.tipOuterW;
                    else if (pos.l < win.l)
                        pos.l = win.l;
                    break;
                default: // 'left' || 'inner-right'
                    pos.l = xL - this.tipOuterW - this.opts.offsetX;
                    if (pos.l < win.l)
                        pos.l = win.l;
                    if (this.opts.alignX == 'left' || this.opts.alignY == 'center')
                        pos.arrow = 'right';
            }
            switch (this.opts.alignY) {
                case 'bottom':
                case 'inner-top':
                    pos.t = yB + this.opts.offsetY;
                    // 'left' and 'right' need priority for 'target'
                    if (!pos.arrow || this.opts.alignTo == 'cursor')
                        pos.arrow = 'top';
                    if (pos.t + this.tipOuterH > win.t + win.h) {
                        pos.t = yT - this.tipOuterH - this.opts.offsetY;
                        if (pos.arrow == 'top')
                            pos.arrow = 'bottom';
                    }
                    break;
                case 'center':
                    pos.t = yC - Math.floor(this.tipOuterH / 2);
                    if (pos.t + this.tipOuterH > win.t + win.h)
                        pos.t = win.t + win.h - this.tipOuterH;
                    else if (pos.t < win.t)
                        pos.t = win.t;
                    break;
                default: // 'top' || 'inner-bottom'
                    pos.t = yT - this.tipOuterH - this.opts.offsetY;
                    // 'left' and 'right' need priority for 'target'
                    if (!pos.arrow || this.opts.alignTo == 'cursor')
                        pos.arrow = 'bottom';
                    if (pos.t < win.t) {
                        pos.t = yB + this.opts.offsetY;
                        if (pos.arrow == 'bottom')
                            pos.arrow = 'top';
                    }
            }
            /*
            switch (this.opts.alignA) {
            case 'left':

                    break;
            case 'right':
            break;
            case 'top':
            break;
            case 'bottom':
            break;
            }
            */
            if (typeof this.opts.beforePosition == 'function') {
                var newPos = this.opts.beforePosition.call(this.$elm[0], pos.l, pos.t, pos.arrow, this.tipOuterW, this.tipOuterH);
                if (newPos === false) {
                    this.isShowOnCondition = true;
                    return;
                } else
                    this.isShowOnCondition = false;

                if (Eu2.Util.isObject(newPos)) {
                    if (newPos['left'])
                        pos.l = newPos['left'];
                    if (newPos['top'])
                        pos.l = newPos['top'];
                    if (newPos['arrow'] && ['left', 'right', 'top', 'bottom'].indexOf(newPos['arrow']) != -1)
                        pos.arrow = newPos['arrow'];
                }
            }

            this.pos = pos;
        }
    };

    $.fn.etip = function(options, content) {
        if (typeof options == 'string') {
            return this.each(function() {
                var etip = $(this).data('etip');
                if (etip && etip[options]) {
                    if (options === 'update')
                        etip[options](content);
                    else
                        etip[options]();
                }
            });
        }

        var opts = $.extend({}, $.fn.etip.defaults, options);

        return this.each(function() {
            new $.etip(this, opts);
        });
    }

    // default settings
    $.fn.etip.defaults = {
        text: null, // content to display ('string', element, function(updateCallback){...}, jQuery)
        bgImageFrameSize: 10, 	// size in pixels for the background-image (if set in CSS) frame around the inner content of the tip
        opacity: null, // the transparent level between 0 and 1
        showTimeout: 1, 	// timeout before showing the tip (in milliseconds 1000 == 1 second)
        hideTimeout: 50, 	// timeout before hiding the tip
        beforePosition: null, // (function(left, top, arrow, width, height)) Call before show tooltip. Use this to change tooltip position when needing
        showOn: 'hover', // handler for showing the tip ('hover', 'focus', 'none') - use 'none' to trigger it manually
        alignTo: 'target', // align/position the tip relative to ('cursor', 'target')
        alignX: 'center', // horizontal alignment for the tip relative to the mouse cursor or the target element
        // ('right', 'center', 'left', 'inner-left', 'inner-right') - 'inner-*' matter if alignTo:'target'
        alignY: 'center', 	// vertical alignment for the tip relative to the mouse cursor or the target element
        // ('bottom', 'center', 'top', 'inner-bottom', 'inner-top') - 'inner-*' matter if alignTo:'target'
        alignA: 'center', // alignment for the arrow (nail) ('left', 'center', 'right') or ('top', 'middle', 'bottom')
        alignC: null,   // text alignment for tooltip's content
        offsetX: 5, 	// offset X pixels from the default position - doesn't matter if alignX:'center'
        offsetY: 5, 	// offset Y pixels from the default position - doesn't matter if alignY:'center'
        allowTipHover: false, 	// allow hovering the tip without hiding it onmouseout of the target - matters only if showOn:'hover'
        followCursor: false, 	// if the tip should follow the cursor - matters only if showOn:'hover' and alignTo:'cursor'
        fade: true, 	// use fade animation
        slide: false, 	// use slide animation
        showCloseButton: false, // Enable close button
        slideOffset: 8, 	// slide animation offset
        showAniDuration: 300, 	// show animation duration - set to 0 if you don't want show animation
        hideAniDuration: 300		// hide animation duration - set to 0 if you don't want hide animation
    };

})(jQuery);

/**
* A Javascript object to encode and/or decode html characters
* @Author R Reid
* source: http://www.strictly-software.com/htmlencode
* Licence: GPL
* 
* Revision:
*  2011-07-14, Jacques-Yves Bleau: 
*       - fixed conversion error with capitalized accentuated characters
*       + converted arr1 and arr2 to object property to remove redundancy
*/

Encoder = {

    // When encoding do we convert characters into html or numerical entities
    EncodeType: "entity",  // entity OR numerical

    isEmpty: function(val) {
        if (val) {
            return ((val === null) || val.length == 0 || /^\s+$/.test(val));
        } else {
            return true;
        }
    },
    arr1: new Array('&nbsp;', '&iexcl;', '&cent;', '&pound;', '&curren;', '&yen;', '&brvbar;', '&sect;', '&uml;', '&copy;', '&ordf;', '&laquo;', '&not;', '&shy;', '&reg;', '&macr;', '&deg;', '&plusmn;', '&sup2;', '&sup3;', '&acute;', '&micro;', '&para;', '&middot;', '&cedil;', '&sup1;', '&ordm;', '&raquo;', '&frac14;', '&frac12;', '&frac34;', '&iquest;', '&Agrave;', '&Aacute;', '&Acirc;', '&Atilde;', '&Auml;', '&Aring;', '&Aelig;', '&Ccedil;', '&Egrave;', '&Eacute;', '&Ecirc;', '&Euml;', '&Igrave;', '&Iacute;', '&Icirc;', '&Iuml;', '&ETH;', '&Ntilde;', '&Ograve;', '&Oacute;', '&Ocirc;', '&Otilde;', '&Ouml;', '&times;', '&Oslash;', '&Ugrave;', '&Uacute;', '&Ucirc;', '&Uuml;', '&Yacute;', '&THORN;', '&szlig;', '&agrave;', '&aacute;', '&acirc;', '&atilde;', '&auml;', '&aring;', '&aelig;', '&ccedil;', '&egrave;', '&eacute;', '&ecirc;', '&euml;', '&igrave;', '&iacute;', '&icirc;', '&iuml;', '&eth;', '&ntilde;', '&ograve;', '&oacute;', '&ocirc;', '&otilde;', '&ouml;', '&divide;', '&Oslash;', '&ugrave;', '&uacute;', '&ucirc;', '&uuml;', '&yacute;', '&thorn;', '&yuml;', '&quot;', '&amp;', '&lt;', '&gt;', '&oelig;', '&oelig;', '&scaron;', '&scaron;', '&yuml;', '&circ;', '&tilde;', '&ensp;', '&emsp;', '&thinsp;', '&zwnj;', '&zwj;', '&lrm;', '&rlm;', '&ndash;', '&mdash;', '&lsquo;', '&rsquo;', '&sbquo;', '&ldquo;', '&rdquo;', '&bdquo;', '&dagger;', '&dagger;', '&permil;', '&lsaquo;', '&rsaquo;', '&euro;', '&fnof;', '&alpha;', '&beta;', '&gamma;', '&delta;', '&epsilon;', '&zeta;', '&eta;', '&theta;', '&iota;', '&kappa;', '&lambda;', '&mu;', '&nu;', '&xi;', '&omicron;', '&pi;', '&rho;', '&sigma;', '&tau;', '&upsilon;', '&phi;', '&chi;', '&psi;', '&omega;', '&alpha;', '&beta;', '&gamma;', '&delta;', '&epsilon;', '&zeta;', '&eta;', '&theta;', '&iota;', '&kappa;', '&lambda;', '&mu;', '&nu;', '&xi;', '&omicron;', '&pi;', '&rho;', '&sigmaf;', '&sigma;', '&tau;', '&upsilon;', '&phi;', '&chi;', '&psi;', '&omega;', '&thetasym;', '&upsih;', '&piv;', '&bull;', '&hellip;', '&prime;', '&prime;', '&oline;', '&frasl;', '&weierp;', '&image;', '&real;', '&trade;', '&alefsym;', '&larr;', '&uarr;', '&rarr;', '&darr;', '&harr;', '&crarr;', '&larr;', '&uarr;', '&rarr;', '&darr;', '&harr;', '&forall;', '&part;', '&exist;', '&empty;', '&nabla;', '&isin;', '&notin;', '&ni;', '&prod;', '&sum;', '&minus;', '&lowast;', '&radic;', '&prop;', '&infin;', '&ang;', '&and;', '&or;', '&cap;', '&cup;', '&int;', '&there4;', '&sim;', '&cong;', '&asymp;', '&ne;', '&equiv;', '&le;', '&ge;', '&sub;', '&sup;', '&nsub;', '&sube;', '&supe;', '&oplus;', '&otimes;', '&perp;', '&sdot;', '&lceil;', '&rceil;', '&lfloor;', '&rfloor;', '&lang;', '&rang;', '&loz;', '&spades;', '&clubs;', '&hearts;', '&diams;'),
    arr2: new Array('&#160;', '&#161;', '&#162;', '&#163;', '&#164;', '&#165;', '&#166;', '&#167;', '&#168;', '&#169;', '&#170;', '&#171;', '&#172;', '&#173;', '&#174;', '&#175;', '&#176;', '&#177;', '&#178;', '&#179;', '&#180;', '&#181;', '&#182;', '&#183;', '&#184;', '&#185;', '&#186;', '&#187;', '&#188;', '&#189;', '&#190;', '&#191;', '&#192;', '&#193;', '&#194;', '&#195;', '&#196;', '&#197;', '&#198;', '&#199;', '&#200;', '&#201;', '&#202;', '&#203;', '&#204;', '&#205;', '&#206;', '&#207;', '&#208;', '&#209;', '&#210;', '&#211;', '&#212;', '&#213;', '&#214;', '&#215;', '&#216;', '&#217;', '&#218;', '&#219;', '&#220;', '&#221;', '&#222;', '&#223;', '&#224;', '&#225;', '&#226;', '&#227;', '&#228;', '&#229;', '&#230;', '&#231;', '&#232;', '&#233;', '&#234;', '&#235;', '&#236;', '&#237;', '&#238;', '&#239;', '&#240;', '&#241;', '&#242;', '&#243;', '&#244;', '&#245;', '&#246;', '&#247;', '&#248;', '&#249;', '&#250;', '&#251;', '&#252;', '&#253;', '&#254;', '&#255;', '&#34;', '&#38;', '&#60;', '&#62;', '&#338;', '&#339;', '&#352;', '&#353;', '&#376;', '&#710;', '&#732;', '&#8194;', '&#8195;', '&#8201;', '&#8204;', '&#8205;', '&#8206;', '&#8207;', '&#8211;', '&#8212;', '&#8216;', '&#8217;', '&#8218;', '&#8220;', '&#8221;', '&#8222;', '&#8224;', '&#8225;', '&#8240;', '&#8249;', '&#8250;', '&#8364;', '&#402;', '&#913;', '&#914;', '&#915;', '&#916;', '&#917;', '&#918;', '&#919;', '&#920;', '&#921;', '&#922;', '&#923;', '&#924;', '&#925;', '&#926;', '&#927;', '&#928;', '&#929;', '&#931;', '&#932;', '&#933;', '&#934;', '&#935;', '&#936;', '&#937;', '&#945;', '&#946;', '&#947;', '&#948;', '&#949;', '&#950;', '&#951;', '&#952;', '&#953;', '&#954;', '&#955;', '&#956;', '&#957;', '&#958;', '&#959;', '&#960;', '&#961;', '&#962;', '&#963;', '&#964;', '&#965;', '&#966;', '&#967;', '&#968;', '&#969;', '&#977;', '&#978;', '&#982;', '&#8226;', '&#8230;', '&#8242;', '&#8243;', '&#8254;', '&#8260;', '&#8472;', '&#8465;', '&#8476;', '&#8482;', '&#8501;', '&#8592;', '&#8593;', '&#8594;', '&#8595;', '&#8596;', '&#8629;', '&#8656;', '&#8657;', '&#8658;', '&#8659;', '&#8660;', '&#8704;', '&#8706;', '&#8707;', '&#8709;', '&#8711;', '&#8712;', '&#8713;', '&#8715;', '&#8719;', '&#8721;', '&#8722;', '&#8727;', '&#8730;', '&#8733;', '&#8734;', '&#8736;', '&#8743;', '&#8744;', '&#8745;', '&#8746;', '&#8747;', '&#8756;', '&#8764;', '&#8773;', '&#8776;', '&#8800;', '&#8801;', '&#8804;', '&#8805;', '&#8834;', '&#8835;', '&#8836;', '&#8838;', '&#8839;', '&#8853;', '&#8855;', '&#8869;', '&#8901;', '&#8968;', '&#8969;', '&#8970;', '&#8971;', '&#9001;', '&#9002;', '&#9674;', '&#9824;', '&#9827;', '&#9829;', '&#9830;'),

    // Convert HTML entities into numerical entities
    HTML2Numerical: function(s) {
        return this.swapArrayVals(s, this.arr1, this.arr2);
    },

    // Convert Numerical entities into HTML entities
    NumericalToHTML: function(s) {
        return this.swapArrayVals(s, this.arr2, this.arr1);
    },


    // Numerically encodes all unicode characters
    numEncode: function(s) {

        if (this.isEmpty(s)) return "";

        var e = "";
        for (var i = 0; i < s.length; i++) {
            var c = s.charAt(i);
            if (c < " " || c > "~") {
                c = "&#" + c.charCodeAt() + ";";
            }
            e += c;
        }
        return e;
    },

    // HTML Decode numerical and HTML entities back to original values
    htmlDecode: function(s) {

        var c, m, d = s;

        if (this.isEmpty(d)) return "";

        // convert HTML entites back to numerical entites first
        d = this.HTML2Numerical(d);

        // look for numerical entities &#34;
        arr = d.match(/&#[0-9]{1,5};/g);

        // if no matches found in string then skip
        if (arr != null) {
            for (var x = 0; x < arr.length; x++) {
                m = arr[x];
                c = m.substring(2, m.length - 1); //get numeric part which is refernce to unicode character
                // if its a valid number we can decode
                if (c >= -32768 && c <= 65535) {
                    // decode every single match within string
                    d = d.replace(m, String.fromCharCode(c));
                } else {
                    d = d.replace(m, ""); //invalid so replace with nada
                }
            }
        }

        return d;
    },

    // encode an input string into either numerical or HTML entities
    htmlEncode: function(s, dbl) {

        if (this.isEmpty(s)) return "";

        // do we allow double encoding? E.g will &amp; be turned into &amp;amp;
        dbl = dbl || false; //default to prevent double encoding

        // if allowing double encoding we do ampersands first
        if (dbl) {
            if (this.EncodeType == "numerical") {
                s = s.replace(/&/g, "&#38;");
            } else {
                s = s.replace(/&/g, "&amp;");
            }
        }

        // convert the xss chars to numerical entities ' " < >
        s = this.XSSEncode(s, false);

        if (this.EncodeType == "numerical" || !dbl) {
            // Now call function that will convert any HTML entities to numerical codes
            s = this.HTML2Numerical(s);
        }

        // Now encode all chars above 127 e.g unicode
        s = this.numEncode(s);

        // now we know anything that needs to be encoded has been converted to numerical entities we
        // can encode any ampersands & that are not part of encoded entities
        // to handle the fact that I need to do a negative check and handle multiple ampersands &&&
        // I am going to use a placeholder

        // if we don't want double encoded entities we ignore the & in existing entities
        if (!dbl) {
            s = s.replace(/&#/g, "##AMPHASH##");

            if (this.EncodeType == "numerical") {
                s = s.replace(/&/g, "&#38;");
            } else {
                s = s.replace(/&/g, "&amp;");
            }

            s = s.replace(/##AMPHASH##/g, "&#");
        }

        // replace any malformed entities
        s = s.replace(/&#\d*([^\d;]|$)/g, "$1");

        if (!dbl) {
            // safety check to correct any double encoded &amp;
            s = this.correctEncoding(s);
        }

        // now do we need to convert our numerical encoded string into entities
        if (this.EncodeType == "entity") {
            s = this.NumericalToHTML(s);
        }

        return s;
    },

    // Encodes the basic 4 characters used to malform HTML in XSS hacks
    XSSEncode: function(s, en) {
        if (!this.isEmpty(s)) {
            en = en || true;
            // do we convert to numerical or html entity?
            if (en) {
                s = s.replace(/\'/g, "&#39;"); //no HTML equivalent as &apos is not cross browser supported
                s = s.replace(/\"/g, "&quot;");
                s = s.replace(/</g, "&lt;");
                s = s.replace(/>/g, "&gt;");
            } else {
                s = s.replace(/\'/g, "&#39;"); //no HTML equivalent as &apos is not cross browser supported
                s = s.replace(/\"/g, "&#34;");
                s = s.replace(/</g, "&#60;");
                s = s.replace(/>/g, "&#62;");
            }
            return s;
        } else {
            return "";
        }
    },

    // returns true if a string contains html or numerical encoded entities
    hasEncoded: function(s) {
        if (/&#[0-9]{1,5};/g.test(s)) {
            return true;
        } else if (/&[A-Z]{2,6};/gi.test(s)) {
            return true;
        } else {
            return false;
        }
    },

    // will remove any unicode characters
    stripUnicode: function(s) {
        return s.replace(/[^\x20-\x7E]/g, "");

    },

    // corrects any double encoded &amp; entities e.g &amp;amp;
    correctEncoding: function(s) {
        return s.replace(/(&amp;)(amp;)+/, "$1");
    },

    // Function to loop through an array swaping each item with the value from another array e.g swap HTML entities with Numericals
    swapArrayVals: function(s, arr1, arr2) {
        if (this.isEmpty(s)) return "";
        var re;
        if (arr1 && arr2) {
            //ShowDebug("in swapArrayVals arr1.length = " + arr1.length + " arr2.length = " + arr2.length)
            // array lengths must match
            if (arr1.length == arr2.length) {
                for (var x = 0, i = arr1.length; x < i; x++) {
                    re = new RegExp(arr1[x], 'g');
                    s = s.replace(re, arr2[x]); //swap arr1 item with matching item from arr2	
                }
            }
        }
        return s;
    },
    inArray: function(item, arr) {
        for (var i = 0, x = arr.length; i < x; i++) {
            if (arr[i] === item) {
                return i;
            }
        }
        return -1;
    }
};

/*
* jQuery Globalization plugin
* http://github.com/nje/jquery-glob
*/
(function ($) {
    var localized = { en: {} };
    localized["default"] = localized.en;

    $.extend({
        findClosestCulture: function (name) {
            var match;
            if (!name) {
                match = $.culture || $.cultures["default"];
            }
            else if ($.isPlainObject(name)) {
                match = name;
            }
            else {
                var cultures = $.cultures,
					list = $.isArray(name) ? name : [name],
					i, l = list.length;
                for (i = 0; i < l; i++) {
                    name = list[i];
                    match = cultures[name];
                    if (match) {
                        return match;
                    }
                }
                for (i = 0; i < l; i++) {
                    name = list[i];
                    do {
                        var index = name.lastIndexOf("-");
                        if (index === -1) {
                            break;
                        }
                        // strip off the last part. e.g. en-US => en
                        name = name.substr(0, index);
                        match = cultures[name];
                        if (match) {
                            return match;
                        }
                    }
                    while (1);
                }
            }
            return match || null;
        },
        preferCulture: function (name) {
            $.culture = $.findClosestCulture(name) || $.cultures["default"];
        },
        localize: function (key, culture, value) {
            if (typeof culture === 'string') {
                culture = culture || "default";
                culture = $.cultures[culture] || { name: culture };
            }
            var local = localized[culture.name];
            if (arguments.length === 3) {
                if (!local) {
                    local = localized[culture.name] = {};
                }
                local[key] = value;
            }
            else {
                if (local) {
                    value = local[key];
                }
                if (typeof value === 'undefined') {
                    var language = localized[culture.language];
                    if (language) {
                        value = language[key];
                    }
                    if (typeof value === 'undefined') {
                        value = localized["default"][key];
                    }
                }
            }
            return typeof value === "undefined" ? null : value;
        },
        format: function (value, format, culture) {
            culture = $.findClosestCulture(culture);
            if (typeof value === "number") {
                value = formatNumber(value, format, culture);
            }
            else if (value instanceof Date) {
                value = formatDate(value, format, culture);
            }
            return value;
        },
        parseInt: function (value, radix, culture) {
            return Math.floor($.parseFloat(value, radix, culture));
        },
        parseFloat: function (value, radix, culture) {
            culture = $.findClosestCulture(culture);
            var ret = NaN,
				nf = culture.numberFormat;

            // trim leading and trailing whitespace
            value = trim(value);

            // allow infinity or hexidecimal
            if (regexInfinity.test(value)) {
                ret = parseFloat(value, radix);
            }
            else if (!radix && regexHex.test(value)) {
                ret = parseInt(value, 16);
            }
            else {
                var signInfo = parseNegativePattern(value, nf, nf.pattern[0]),
					sign = signInfo[0],
					num = signInfo[1];
                // determine sign and number
                if (sign === "" && nf.pattern[0] !== "-n") {
                    signInfo = parseNegativePattern(value, nf, "-n");
                    sign = signInfo[0];
                    num = signInfo[1];
                }
                sign = sign || "+";
                // determine exponent and number
                var exponent,
					intAndFraction,
					exponentPos = num.indexOf('e');
                if (exponentPos < 0) exponentPos = num.indexOf('E');
                if (exponentPos < 0) {
                    intAndFraction = num;
                    exponent = null;
                }
                else {
                    intAndFraction = num.substr(0, exponentPos);
                    exponent = num.substr(exponentPos + 1);
                }
                // determine decimal position
                var integer,
					fraction,
					decSep = nf['.'],
					decimalPos = intAndFraction.indexOf(decSep);
                if (decimalPos < 0) {
                    integer = intAndFraction;
                    fraction = null;
                }
                else {
                    integer = intAndFraction.substr(0, decimalPos);
                    fraction = intAndFraction.substr(decimalPos + decSep.length);
                }
                // handle groups (e.g. 1,000,000)
                var groupSep = nf[","];
                integer = integer.split(groupSep).join('');
                var altGroupSep = groupSep.replace(/\u00A0/g, " ");
                if (groupSep !== altGroupSep) {
                    integer = integer.split(altGroupSep).join('');
                }
                // build a natively parsable number string
                var p = sign + integer;
                if (fraction !== null) {
                    p += '.' + fraction;
                }
                if (exponent !== null) {
                    // exponent itself may have a number patternd
                    var expSignInfo = parseNegativePattern(exponent, nf, "-n");
                    p += 'e' + (expSignInfo[0] || "+") + expSignInfo[1];
                }
                if (regexParseFloat.test(p)) {
                    ret = parseFloat(p);
                }
            }
            return ret;
        },
        parseDate: function (value, formats, culture) {
            culture = $.findClosestCulture(culture);

            var date;
            if (formats) {
                if (typeof formats === "string") {
                    formats = [formats];
                }
                if (formats.length) {
                    for (var i = 0, l = formats.length; i < l; i++) {
                        var format = formats[i];
                        if (format) {
                            date = parseExact(value, format, culture);
                            if (date) {
                                break;
                            }
                        }
                    }
                }
            }
            else {
                $.each(culture.calendar.patterns, function (name, format) {
                    date = parseExact(value, format, culture);
                    if (date) {
                        return false;
                    }
                });
            }
            return date || null;
        }
    });

    // 1.    When defining a culture, all fields are required except the ones stated as optional.
    // 2.    You can use $.extend to copy an existing culture and provide only the differing values,
    //       a good practice since most cultures do not differ too much from the 'default' culture.
    //       DO use the 'default' culture if you do this, as it is the only one that definitely
    //       exists.
    // 3.    Other plugins may add to the culture information provided by extending it. However,
    //       that plugin may extend it prior to the culture being defined, or after. Therefore,
    //       do not overwrite values that already exist when defining the baseline for a culture,
    //       by extending your culture object with the existing one.
    // 4.    Each culture should have a ".calendars" object with at least one calendar named "standard"
    //       which serves as the default calendar in use by that culture.
    // 5.    Each culture should have a ".calendar" object which is the current calendar being used,
    //       it may be dynamically changed at any time to one of the calendars in ".calendars".

    // To define a culture, use the following pattern, which handles defining the culture based
    // on the 'default culture, extending it with the existing culture if it exists, and defining
    // it if it does not exist.
    // $.cultures.foo = $.extend(true, $.extend(true, {}, $.cultures['default'], fooCulture), $.cultures.foo)

    var cultures = $.cultures = $.cultures || {};
    var en = cultures["default"] = cultures.en = $.extend(true, {
        // A unique name for the culture in the form <language code>-<country/region code>
        name: "en",
        // the name of the culture in the english language
        englishName: "English",
        // the name of the culture in its own language
        nativeName: "English",
        // whether the culture uses right-to-left text
        isRTL: false,
        // 'language' is used for so-called "specific" cultures.
        // For example, the culture "es-CL" means "Spanish, in Chili".
        // It represents the Spanish-speaking culture as it is in Chili,
        // which might have different formatting rules or even translations
        // than Spanish in Spain. A "neutral" culture is one that is not
        // specific to a region. For example, the culture "es" is the generic
        // Spanish culture, which may be a more generalized version of the language
        // that may or may not be what a specific culture expects.
        // For a specific culture like "es-CL", the 'language' field refers to the
        // neutral, generic culture information for the language it is using.
        // This is not always a simple matter of the string before the dash.
        // For example, the "zh-Hans" culture is netural (Simplified Chinese).
        // And the 'zh-SG' culture is Simplified Chinese in Singapore, whose lanugage
        // field is "zh-CHS", not "zh".
        // This field should be used to navigate from a specific culture to it's
        // more general, neutral culture. If a culture is already as general as it 
        // can get, the language may refer to itself.
        language: "en",
        // numberFormat defines general number formatting rules, like the digits in
        // each grouping, the group separator, and how negative numbers are displayed.
        numberFormat: {
            // [negativePattern]
            // Note, numberFormat.pattern has no 'positivePattern' unlike percent and currency,
            // but is still defined as an array for consistency with them.
            //  negativePattern: one of "(n)|-n|- n|n-|n -"
            pattern: ["-n"],
            // number of decimal places normally shown
            decimals: 2,
            // string that separates number groups, as in 1,000,000
            ',': ",",
            // string that separates a number from the fractional portion, as in 1.99
            '.': ".",
            // array of numbers indicating the size of each number group.
            // TODO: more detailed description and example
            groupSizes: [3],
            // symbol used for positive numbers
            '+': "+",
            // symbol used for negative numbers
            '-': "-",
            percent: {
                // [negativePattern, positivePattern]
                //     negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
                //     positivePattern: one of "n %|n%|%n|% n"
                pattern: ["-n %", "n %"],
                // number of decimal places normally shown
                decimals: 2,
                // array of numbers indicating the size of each number group.
                // TODO: more detailed description and example
                groupSizes: [3],
                // string that separates number groups, as in 1,000,000
                ',': ",",
                // string that separates a number from the fractional portion, as in 1.99
                '.': ".",
                // symbol used to represent a percentage
                symbol: "%"
            },
            currency: {
                // [negativePattern, positivePattern]
                //     negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
                //     positivePattern: one of "$n|n$|$ n|n $"
                pattern: ["($n)", "$n"],
                // number of decimal places normally shown
                decimals: 2,
                // array of numbers indicating the size of each number group.
                // TODO: more detailed description and example
                groupSizes: [3],
                // string that separates number groups, as in 1,000,000
                ',': ",",
                // string that separates a number from the fractional portion, as in 1.99
                '.': ".",
                // symbol used to represent currency
                symbol: "$"
            }
        },
        // calendars defines all the possible calendars used by this culture.
        // There should be at least one defined with name 'standard', and is the default
        // calendar used by the culture.
        // A calendar contains information about how dates are formatted, information about
        // the calendar's eras, a standard set of the date formats,
        // translations for day and month names, and if the calendar is not based on the Gregorian
        // calendar, conversion functions to and from the Gregorian calendar.
        calendars: {
            standard: {
                // name that identifies the type of calendar this is
                name: "Gregorian_USEnglish",
                // separator of parts of a date (e.g. '/' in 11/05/1955)
                '/': "/",
                // separator of parts of a time (e.g. ':' in 05:44 PM)
                ':': ":",
                // the first day of the week (0 = Sunday, 1 = Monday, etc)
                firstDay: 0,
                days: {
                    // full day names
                    names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
                    // abbreviated day names
                    namesAbbr: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
                    // shortest day names
                    namesShort: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]
                },
                months: {
                    // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
                    names: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ""],
                    // abbreviated month names
                    namesAbbr: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""]
                },
                // AM and PM designators in one of these forms:
                // The usual view, and the upper and lower case versions
                //      [standard,lowercase,uppercase] 
                // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
                //      null
                AM: ["AM", "am", "AM"],
                PM: ["PM", "pm", "PM"],
                eras: [
                // eras in reverse chronological order.
                // name: the name of the era in this culture (e.g. A.D., C.E.)
                // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
                // offset: offset in years from gregorian calendar
					{"name": "A.D.", "start": null, "offset": 0 }
				],
                // when a two digit year is given, it will never be parsed as a four digit
                // year greater than this year (in the appropriate era for the culture)
                twoDigitYearMax: 2029,
                // set of predefined date and time patterns used by the culture
                // these represent the format someone in this culture would expect
                // to see given the portions of the date that are shown.
                patterns: {
                    // short date pattern
                    d: "M/d/yyyy",
                    // long date pattern
                    D: "dddd, MMMM dd, yyyy",
                    // short time pattern
                    t: "h:mm tt",
                    // long time pattern
                    T: "h:mm:ss tt",
                    // long date, short time pattern
                    f: "dddd, MMMM dd, yyyy h:mm tt",
                    // long date, long time pattern
                    F: "dddd, MMMM dd, yyyy h:mm:ss tt",
                    // month/day pattern
                    M: "MMMM dd",
                    // month/year pattern
                    Y: "yyyy MMMM",
                    // S is a sortable format that does not vary by culture
                    S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
                }
                // optional fields for each calendar:
                /*
                monthsGenitive:
                Same as months but used when the day preceeds the month.
                Omit if the culture has no genitive distinction in month names.
                For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
                convert:
                Allows for the support of non-gregorian based calendars. This convert object is used to
                to convert a date to and from a gregorian calendar date to handle parsing and formatting.
                The two functions:
                fromGregorian(date)
                Given the date as a parameter, return an array with parts [year, month, day]
                corresponding to the non-gregorian based year, month, and day for the calendar.
                toGregorian(year, month, day)
                Given the non-gregorian year, month, and day, return a new Date() object 
                set to the corresponding date in the gregorian calendar.
                */
            }
        }
    }, cultures.en);
    en.calendar = en.calendar || en.calendars.standard;

    var regexTrim = /^\s+|\s+$/g,
		regexInfinity = /^[+-]?infinity$/i,
		regexHex = /^0x[a-f0-9]+$/i,
		regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/;

    function startsWith(value, pattern) {
        return value.indexOf(pattern) === 0;
    }

    function endsWith(value, pattern) {
        return value.substr(value.length - pattern.length) === pattern;
    }

    function trim(value) {
        return (value + "").replace(regexTrim, "");
    }

    function zeroPad(str, count, left) {
        for (var l = str.length; l < count; l++) {
            str = (left ? ('0' + str) : (str + '0'));
        }
        return str;
    }

    // *************************************** Numbers ***************************************

    function expandNumber(number, precision, formatInfo) {
        var groupSizes = formatInfo.groupSizes,
			curSize = groupSizes[0],
			curGroupIndex = 1,
			factor = Math.pow(10, precision),
			rounded = Math.round(number * factor) / factor;
        if (!isFinite(rounded)) {
            rounded = number;
        }
        number = rounded;

        var numberString = number + "",
			right = "",
			split = numberString.split(/e/i),
			exponent = split.length > 1 ? parseInt(split[1], 10) : 0;
        numberString = split[0];
        split = numberString.split(".");
        numberString = split[0];
        right = split.length > 1 ? split[1] : "";

        var l;
        if (exponent > 0) {
            right = zeroPad(right, exponent, false);
            numberString += right.slice(0, exponent);
            right = right.substr(exponent);
        }
        else if (exponent < 0) {
            exponent = -exponent;
            numberString = zeroPad(numberString, exponent + 1);
            right = numberString.slice(-exponent, numberString.length) + right;
            numberString = numberString.slice(0, -exponent);
        }

        if (precision > 0) {
            right = formatInfo['.'] +
				((right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision));
        }
        else {
            right = "";
        }

        var stringIndex = numberString.length - 1,
			sep = formatInfo[","],
			ret = "";

        while (stringIndex >= 0) {
            if (curSize === 0 || curSize > stringIndex) {
                return numberString.slice(0, stringIndex + 1) + (ret.length ? (sep + ret + right) : right);
            }
            ret = numberString.slice(stringIndex - curSize + 1, stringIndex + 1) + (ret.length ? (sep + ret) : "");

            stringIndex -= curSize;

            if (curGroupIndex < groupSizes.length) {
                curSize = groupSizes[curGroupIndex];
                curGroupIndex++;
            }
        }
        return numberString.slice(0, stringIndex + 1) + sep + ret + right;
    }


    function parseNegativePattern(value, nf, negativePattern) {
        var neg = nf["-"],
			pos = nf["+"],
			ret;
        switch (negativePattern) {
            case "n -":
                neg = ' ' + neg;
                pos = ' ' + pos;
                // fall through
            case "n-":
                if (endsWith(value, neg)) {
                    ret = ['-', value.substr(0, value.length - neg.length)];
                }
                else if (endsWith(value, pos)) {
                    ret = ['+', value.substr(0, value.length - pos.length)];
                }
                break;
            case "- n":
                neg += ' ';
                pos += ' ';
                // fall through
            case "-n":
                if (startsWith(value, neg)) {
                    ret = ['-', value.substr(neg.length)];
                }
                else if (startsWith(value, pos)) {
                    ret = ['+', value.substr(pos.length)];
                }
                break;
            case "(n)":
                if (startsWith(value, '(') && endsWith(value, ')')) {
                    ret = ['-', value.substr(1, value.length - 2)];
                }
                break;
        }
        return ret || ['', value];
    }

    function formatNumber(value, format, culture) {
        if (!format || format === 'i') {
            return culture.name.length ? value.toLocaleString() : value.toString();
        }
        format = format || "D";

        var nf = culture.numberFormat,
			number = Math.abs(value),
			precision = -1,
			pattern;
        if (format.length > 1) precision = parseInt(format.slice(1), 10);

        var current = format.charAt(0).toUpperCase(),
			formatInfo;

        switch (current) {
            case "D":
                pattern = 'n';
                if (precision !== -1) {
                    number = zeroPad("" + number, precision, true);
                }
                if (value < 0) number = -number;
                break;
            case "N":
                formatInfo = nf;
                // fall through
            case "C":
                formatInfo = formatInfo || nf.currency;
                // fall through
            case "P":
                formatInfo = formatInfo || nf.percent;
                pattern = value < 0 ? formatInfo.pattern[0] : (formatInfo.pattern[1] || "n");
                if (precision === -1) precision = formatInfo.decimals;
                number = expandNumber(number * (current === "P" ? 100 : 1), precision, formatInfo);
                break;
            default:
                $.error("Bad number format specifier: " + current);
        }

        var patternParts = /n|\$|-|%/g,
			ret = "";
        for (; ; ) {
            var index = patternParts.lastIndex,
				ar = patternParts.exec(pattern);

            ret += pattern.slice(index, ar ? ar.index : pattern.length);

            if (!ar) {
                break;
            }

            switch (ar[0]) {
                case "n":
                    ret += number;
                    break;
                case "$":
                    ret += nf.currency.symbol;
                    break;
                case "-":
                    // don't make 0 negative
                    if (/[1-9]/.test(number)) {
                        ret += nf["-"];
                    }
                    break;
                case "%":
                    ret += nf.percent.symbol;
                    break;
            }
        }

        return ret;
    }

    // *************************************** Dates ***************************************

    function outOfRange(value, low, high) {
        return value < low || value > high;
    }

    function expandYear(cal, year) {
        // expands 2-digit year into 4 digits.
        var now = new Date(),
			era = getEra(now);
        if (year < 100) {
            var twoDigitYearMax = cal.twoDigitYearMax;
            twoDigitYearMax = typeof twoDigitYearMax === 'string' ? new Date().getFullYear() % 100 + parseInt(twoDigitYearMax, 10) : twoDigitYearMax;
            var curr = getEraYear(now, cal, era);
            year += curr - (curr % 100);
            if (year > twoDigitYearMax) {
                year -= 100;
            }
        }
        return year;
    }

    function getEra(date, eras) {
        if (!eras) return 0;
        var start, ticks = date.getTime();
        for (var i = 0, l = eras.length; i < l; i++) {
            start = eras[i].start;
            if (start === null || ticks >= start) {
                return i;
            }
        }
        return 0;
    }

    function toUpper(value) {
        // 'he-IL' has non-breaking space in weekday names.
        return value.split("\u00A0").join(' ').toUpperCase();
    }

    function toUpperArray(arr) {
        return $.map(arr, function (e) {
            return toUpper(e);
        });
    }

    function getEraYear(date, cal, era, sortable) {
        var year = date.getFullYear();
        if (!sortable && cal.eras) {
            // convert normal gregorian year to era-shifted gregorian
            // year by subtracting the era offset
            year -= cal.eras[era].offset;
        }
        return year;
    }

    function getDayIndex(cal, value, abbr) {
        var ret,
			days = cal.days,
			upperDays = cal._upperDays;
        if (!upperDays) {
            cal._upperDays = upperDays = [
				toUpperArray(days.names),
				toUpperArray(days.namesAbbr),
				toUpperArray(days.namesShort)
			];
        }
        value = toUpper(value);
        if (abbr) {
            ret = $.inArray(value, upperDays[1]);
            if (ret === -1) {
                ret = $.inArray(value, upperDays[2]);
            }
        }
        else {
            ret = $.inArray(value, upperDays[0]);
        }
        return ret;
    }

    function getMonthIndex(cal, value, abbr) {
        var months = cal.months,
			monthsGen = cal.monthsGenitive || cal.months,
			upperMonths = cal._upperMonths,
			upperMonthsGen = cal._upperMonthsGen;
        if (!upperMonths) {
            cal._upperMonths = upperMonths = [
				toUpperArray(months.names),
				toUpperArray(months.namesAbbr),
			];
            cal._upperMonthsGen = upperMonthsGen = [
				toUpperArray(monthsGen.names),
				toUpperArray(monthsGen.namesAbbr)
			];
        }
        value = toUpper(value);
        var i = $.inArray(value, abbr ? upperMonths[1] : upperMonths[0]);
        if (i < 0) {
            i = $.inArray(value, abbr ? upperMonthsGen[1] : upperMonthsGen[0]);
        }
        return i;
    }

    function appendPreOrPostMatch(preMatch, strings) {
        // appends pre- and post- token match strings while removing escaped characters.
        // Returns a single quote count which is used to determine if the token occurs
        // in a string literal.
        var quoteCount = 0,
			escaped = false;
        for (var i = 0, il = preMatch.length; i < il; i++) {
            var c = preMatch.charAt(i);
            switch (c) {
                case '\'':
                    if (escaped) {
                        strings.push("'");
                    }
                    else {
                        quoteCount++;
                    }
                    escaped = false;
                    break;
                case '\\':
                    if (escaped) {
                        strings.push("\\");
                    }
                    escaped = !escaped;
                    break;
                default:
                    strings.push(c);
                    escaped = false;
                    break;
            }
        }
        return quoteCount;
    }

    function expandFormat(cal, format) {
        // expands unspecified or single character date formats into the full pattern.
        format = format || "F";
        var pattern,
			patterns = cal.patterns,
			len = format.length;
        if (len === 1) {
            pattern = patterns[format];
            if (!pattern) {
                $.error("Invalid date format string '" + format + "'.");
            }
            format = pattern;
        }
        else if (len === 2 && format.charAt(0) === "%") {
            // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
            format = format.charAt(1);
        }
        return format;
    }

    function getParseRegExp(cal, format) {
        // converts a format string into a regular expression with groups that
        // can be used to extract date fields from a date string.
        // check for a cached parse regex.
        var re = cal._parseRegExp;
        if (!re) {
            cal._parseRegExp = re = {};
        }
        else {
            var reFormat = re[format];
            if (reFormat) {
                return reFormat;
            }
        }

        // expand single digit formats, then escape regular expression characters.
        var expFormat = expandFormat(cal, format).replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1"),
			regexp = ["^"],
			groups = [],
			index = 0,
			quoteCount = 0,
			tokenRegExp = getTokenRegExp(),
			match;

        // iterate through each date token found.
        while ((match = tokenRegExp.exec(expFormat)) !== null) {
            var preMatch = expFormat.slice(index, match.index);
            index = tokenRegExp.lastIndex;

            // don't replace any matches that occur inside a string literal.
            quoteCount += appendPreOrPostMatch(preMatch, regexp);
            if (quoteCount % 2) {
                regexp.push(match[0]);
                continue;
            }

            // add a regex group for the token.
            var m = match[0],
				len = m.length,
				add;
            switch (m) {
                case 'dddd': case 'ddd':
                case 'MMMM': case 'MMM':
                case 'gg': case 'g':
                    add = "(\\D+)";
                    break;
                case 'tt': case 't':
                    add = "(\\D*)";
                    break;
                case 'yyyy':
                case 'fff':
                case 'ff':
                case 'f':
                    add = "(\\d{" + len + "})";
                    break;
                case 'dd': case 'd':
                case 'MM': case 'M':
                case 'yy': case 'y':
                case 'HH': case 'H':
                case 'hh': case 'h':
                case 'mm': case 'm':
                case 'ss': case 's':
                    add = "(\\d\\d?)";
                    break;
                case 'zzz':
                    add = "([+-]?\\d\\d?:\\d{2})";
                    break;
                case 'zz': case 'z':
                    add = "([+-]?\\d\\d?)";
                    break;
                case '/':
                    add = "(\\" + cal["/"] + ")";
                    break;
                default:
                    $.error("Invalid date format pattern '" + m + "'.");
                    break;
            }
            if (add) {
                regexp.push(add);
            }
            groups.push(match[0]);
        }
        appendPreOrPostMatch(expFormat.slice(index), regexp);
        regexp.push("$");

        // allow whitespace to differ when matching formats.
        var regexpStr = regexp.join('').replace(/\s+/g, "\\s+"),
			parseRegExp = { 'regExp': regexpStr, 'groups': groups };

        // cache the regex for this format.
        return re[format] = parseRegExp;
    }

    function getTokenRegExp() {
        // regular expression for matching date and time tokens in format strings.
        return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g;
    }

    function parseExact(value, format, culture) {
        // try to parse the date string by matching against the format string
        // while using the specified culture for date field names.
        value = trim(value);
        var cal = culture.calendar,
        // convert date formats into regular expressions with groupings.
        // use the regexp to determine the input format and extract the date fields.
			parseInfo = getParseRegExp(cal, format),
			match = new RegExp(parseInfo.regExp).exec(value);
        if (match === null) {
            return null;
        }
        // found a date format that matches the input.
        var groups = parseInfo.groups,
			era = null, year = null, month = null, date = null, weekDay = null,
			hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
			pmHour = false;
        // iterate the format groups to extract and set the date fields.
        for (var j = 0, jl = groups.length; j < jl; j++) {
            var matchGroup = match[j + 1];
            if (matchGroup) {
                var current = groups[j],
					clength = current.length,
					matchInt = parseInt(matchGroup, 10);
                switch (current) {
                    case 'dd': case 'd':
                        // Day of month.
                        date = matchInt;
                        // check that date is generally in valid range, also checking overflow below.
                        if (outOfRange(date, 1, 31)) return null;
                        break;
                    case 'MMM':
                    case 'MMMM':
                        month = getMonthIndex(cal, matchGroup, clength === 3);
                        if (outOfRange(month, 0, 11)) return null;
                        break;
                    case 'M': case 'MM':
                        // Month.
                        month = matchInt - 1;
                        if (outOfRange(month, 0, 11)) return null;
                        break;
                    case 'y': case 'yy':
                    case 'yyyy':
                        year = clength < 4 ? expandYear(cal, matchInt) : matchInt;
                        if (outOfRange(year, 0, 9999)) return null;
                        break;
                    case 'h': case 'hh':
                        // Hours (12-hour clock).
                        hour = matchInt;
                        if (hour === 12) hour = 0;
                        if (outOfRange(hour, 0, 11)) return null;
                        break;
                    case 'H': case 'HH':
                        // Hours (24-hour clock).
                        hour = matchInt;
                        if (outOfRange(hour, 0, 23)) return null;
                        break;
                    case 'm': case 'mm':
                        // Minutes.
                        min = matchInt;
                        if (outOfRange(min, 0, 59)) return null;
                        break;
                    case 's': case 'ss':
                        // Seconds.
                        sec = matchInt;
                        if (outOfRange(sec, 0, 59)) return null;
                        break;
                    case 'tt': case 't':
                        // AM/PM designator.
                        // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
                        // the AM tokens. If not, fail the parse for this format.
                        pmHour = cal.PM && (matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2]);
                        if (!pmHour && (!cal.AM || (matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2]))) return null;
                        break;
                    case 'f':
                        // Deciseconds.
                    case 'ff':
                        // Centiseconds.
                    case 'fff':
                        // Milliseconds.
                        msec = matchInt * Math.pow(10, 3 - clength);
                        if (outOfRange(msec, 0, 999)) return null;
                        break;
                    case 'ddd':
                        // Day of week.
                    case 'dddd':
                        // Day of week.
                        weekDay = getDayIndex(cal, matchGroup, clength === 3);
                        if (outOfRange(weekDay, 0, 6)) return null;
                        break;
                    case 'zzz':
                        // Time zone offset in +/- hours:min.
                        var offsets = matchGroup.split(/:/);
                        if (offsets.length !== 2) return null;
                        hourOffset = parseInt(offsets[0], 10);
                        if (outOfRange(hourOffset, -12, 13)) return null;
                        var minOffset = parseInt(offsets[1], 10);
                        if (outOfRange(minOffset, 0, 59)) return null;
                        tzMinOffset = (hourOffset * 60) + (startsWith(matchGroup, '-') ? -minOffset : minOffset);
                        break;
                    case 'z': case 'zz':
                        // Time zone offset in +/- hours.
                        hourOffset = matchInt;
                        if (outOfRange(hourOffset, -12, 13)) return null;
                        tzMinOffset = hourOffset * 60;
                        break;
                    case 'g': case 'gg':
                        var eraName = matchGroup;
                        if (!eraName || !cal.eras) return null;
                        eraName = trim(eraName.toLowerCase());
                        for (var i = 0, l = cal.eras.length; i < l; i++) {
                            if (eraName === cal.eras[i].name.toLowerCase()) {
                                era = i;
                                break;
                            }
                        }
                        // could not find an era with that name
                        if (era === null) return null;
                        break;
                }
            }
        }
        var result = new Date(), defaultYear, convert = cal.convert;
        defaultYear = convert ? convert.fromGregorian(result)[0] : result.getFullYear();
        if (year === null) {
            year = defaultYear;
        }
        else if (cal.eras) {
            // year must be shifted to normal gregorian year
            // but not if year was not specified, its already normal gregorian
            // per the main if clause above.
            year += cal.eras[(era || 0)].offset;
        }
        // set default day and month to 1 and January, so if unspecified, these are the defaults
        // instead of the current day/month.
        if (month === null) {
            month = 0;
        }
        if (date === null) {
            date = 1;
        }
        // now have year, month, and date, but in the culture's calendar.
        // convert to gregorian if necessary
        if (convert) {
            result = convert.toGregorian(year, month, date);
            // conversion failed, must be an invalid match
            if (result === null) return null;
        }
        else {
            // have to set year, month and date together to avoid overflow based on current date.
            result.setFullYear(year, month, date);
            // check to see if date overflowed for specified month (only checked 1-31 above).
            if (result.getDate() !== date) return null;
            // invalid day of week.
            if (weekDay !== null && result.getDay() !== weekDay) {
                return null;
            }
        }
        // if pm designator token was found make sure the hours fit the 24-hour clock.
        if (pmHour && hour < 12) {
            hour += 12;
        }
        result.setHours(hour, min, sec, msec);
        if (tzMinOffset !== null) {
            // adjust timezone to utc before applying local offset.
            var adjustedMin = result.getMinutes() - (tzMinOffset + result.getTimezoneOffset());
            // Safari limits hours and minutes to the range of -127 to 127.  We need to use setHours
            // to ensure both these fields will not exceed this range.  adjustedMin will range
            // somewhere between -1440 and 1500, so we only need to split this into hours.
            result.setHours(result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60);
        }
        return result;
    }

    function formatDate(value, format, culture) {
        var cal = culture.calendar,
			convert = cal.convert;
        if (!format || !format.length || format === 'i') {
            var ret;
            if (culture && culture.name.length) {
                if (convert) {
                    // non-gregorian calendar, so we cannot use built-in toLocaleString()
                    ret = formatDate(value, cal.patterns.F, culture);
                }
                else {
                    var eraDate = new Date(value.getTime()),
						era = getEra(value, cal.eras);
                    eraDate.setFullYear(getEraYear(value, cal, era));
                    ret = eraDate.toLocaleString();
                }
            }
            else {
                ret = value.toString();
            }
            return ret;
        }

        var eras = cal.eras,
			sortable = format === "s";
        format = expandFormat(cal, format);

        // Start with an empty string
        ret = [];
        var hour,
			zeros = ['0', '00', '000'],
			foundDay,
			checkedDay,
			dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
			quoteCount = 0,
			tokenRegExp = getTokenRegExp(),
			converted;

        function padZeros(num, c) {
            var r, s = num + '';
            if (c > 1 && s.length < c) {
                r = (zeros[c - 2] + s);
                return r.substr(r.length - c, c);
            }
            else {
                r = s;
            }
            return r;
        }

        function hasDay() {
            if (foundDay || checkedDay) {
                return foundDay;
            }
            foundDay = dayPartRegExp.test(format);
            checkedDay = true;
            return foundDay;
        }

        function getPart(date, part) {
            if (converted) {
                return converted[part];
            }
            switch (part) {
                case 0: return date.getFullYear();
                case 1: return date.getMonth();
                case 2: return date.getDate();
            }
        }

        if (!sortable && convert) {
            converted = convert.fromGregorian(value);
        }

        for (; ; ) {
            // Save the current index
            var index = tokenRegExp.lastIndex,
            // Look for the next pattern
				ar = tokenRegExp.exec(format);

            // Append the text before the pattern (or the end of the string if not found)
            var preMatch = format.slice(index, ar ? ar.index : format.length);
            quoteCount += appendPreOrPostMatch(preMatch, ret);

            if (!ar) {
                break;
            }

            // do not replace any matches that occur inside a string literal.
            if (quoteCount % 2) {
                ret.push(ar[0]);
                continue;
            }

            var current = ar[0],
				clength = current.length;

            switch (current) {
                case "ddd":
                    //Day of the week, as a three-letter abbreviation
                case "dddd":
                    // Day of the week, using the full name
                    names = (clength === 3) ? cal.days.namesAbbr : cal.days.names;
                    ret.push(names[value.getDay()]);
                    break;
                case "d":
                    // Day of month, without leading zero for single-digit days
                case "dd":
                    // Day of month, with leading zero for single-digit days
                    foundDay = true;
                    ret.push(padZeros(getPart(value, 2), clength));
                    break;
                case "MMM":
                    // Month, as a three-letter abbreviation
                case "MMMM":
                    // Month, using the full name
                    var part = getPart(value, 1);
                    ret.push((cal.monthsGenitive && hasDay())
						? cal.monthsGenitive[clength === 3 ? "namesAbbr" : "names"][part]
						: cal.months[clength === 3 ? "namesAbbr" : "names"][part]);
                    break;
                case "M":
                    // Month, as digits, with no leading zero for single-digit months
                case "MM":
                    // Month, as digits, with leading zero for single-digit months
                    ret.push(padZeros(getPart(value, 1) + 1, clength));
                    break;
                case "y":
                    // Year, as two digits, but with no leading zero for years less than 10
                case "yy":
                    // Year, as two digits, with leading zero for years less than 10
                case "yyyy":
                    // Year represented by four full digits
                    part = converted ? converted[0] : getEraYear(value, cal, getEra(value, eras), sortable);
                    if (clength < 4) {
                        part = part % 100;
                    }
                    ret.push(padZeros(part, clength));
                    break;
                case "h":
                    // Hours with no leading zero for single-digit hours, using 12-hour clock
                case "hh":
                    // Hours with leading zero for single-digit hours, using 12-hour clock
                    hour = value.getHours() % 12;
                    if (hour === 0) hour = 12;
                    ret.push(padZeros(hour, clength));
                    break;
                case "H":
                    // Hours with no leading zero for single-digit hours, using 24-hour clock
                case "HH":
                    // Hours with leading zero for single-digit hours, using 24-hour clock
                    ret.push(padZeros(value.getHours(), clength));
                    break;
                case "m":
                    // Minutes with no leading zero  for single-digit minutes
                case "mm":
                    // Minutes with leading zero  for single-digit minutes
                    ret.push(padZeros(value.getMinutes(), clength));
                    break;
                case "s":
                    // Seconds with no leading zero for single-digit seconds
                case "ss":
                    // Seconds with leading zero for single-digit seconds
                    ret.push(padZeros(value.getSeconds(), clength));
                    break;
                case "t":
                    // One character am/pm indicator ("a" or "p")
                case "tt":
                    // Multicharacter am/pm indicator
                    part = value.getHours() < 12 ? (cal.AM ? cal.AM[0] : " ") : (cal.PM ? cal.PM[0] : " ");
                    ret.push(clength === 1 ? part.charAt(0) : part);
                    break;
                case "f":
                    // Deciseconds
                case "ff":
                    // Centiseconds
                case "fff":
                    // Milliseconds
                    ret.push(padZeros(value.getMilliseconds(), 3).substr(0, clength));
                    break;
                case "z":
                    // Time zone offset, no leading zero
                case "zz":
                    // Time zone offset with leading zero
                    hour = value.getTimezoneOffset() / 60;
                    ret.push((hour <= 0 ? '+' : '-') + padZeros(Math.floor(Math.abs(hour)), clength));
                    break;
                case "zzz":
                    // Time zone offset with leading zero
                    hour = value.getTimezoneOffset() / 60;
                    ret.push((hour <= 0 ? '+' : '-') + padZeros(Math.floor(Math.abs(hour)), 2) +
                    // Hard coded ":" separator, rather than using cal.TimeSeparator
                    // Repeated here for consistency, plus ":" was already assumed in date parsing.
						":" + padZeros(Math.abs(value.getTimezoneOffset() % 60), 2));
                    break;
                case "g":
                case "gg":
                    if (cal.eras) {
                        ret.push(cal.eras[getEra(value, eras)].name);
                    }
                    break;
                case "/":
                    ret.push(cal["/"]);
                    break;
                default:
                    $.error("Invalid date format pattern '" + current + "'.");
                    break;
            }
        }
        return ret.join('');
    }
})(jQuery);

/**
* [30-October-2012][binh.nguyen]
* - Replace old jQuery format plugin with jQuery Globalization (by Microsoft)
* - This script is to make jQuery Globalization compatible with old jQuery format plugin
* - The culture of jQuery Globalization is generated automatically from server using C#
*/
(function (jQ) {
    jQ.extend(jQ.format, {
        number: function (number, format) {
            var defaultCulture = jQ.cultures.defaultCultureName || 'en';
            return jQ.format(number, format || 'N2', defaultCulture);
        },
        date: function (date, format) {
            var defaultCulture = jQ.cultures.defaultCultureName || 'en';
            return jQ.format(date, format || $.cultures[defaultCulture].calendars.standard.patterns['d'], defaultCulture);
        },
        parseInt: function(value, radix) {
            var defaultCulture = jQ.cultures.defaultCultureName || 'en';
            return $.parseInt(value, radix, defaultCulture);
        },
        parseFloat: function(value, radix) {
            var defaultCulture = jQ.cultures.defaultCultureName || 'en';
            return $.parseFloat(value, radix, defaultCulture);
        },
        parseDate: function(value, formats) {
            var defaultCulture = jQ.cultures.defaultCultureName || 'en';
            return $.parseDate(value, formats || $.cultures[defaultCulture].calendars.standard.patterns['d'], defaultCulture);
        }
    });
})(jQuery);